/*
 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012,
 * 2013, 2014 Robert Lougher <rob@jamvm.org.uk>.
 *
 * This file is part of JamVM.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2,
 * or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

#include "jam.h"
#include "hash.h"
#include "class.h"
#include "symbol.h"
#include "excep.h"
#include "thread.h"
#include "classlib.h"

#ifdef USE_ZIP
#define BCP_MESSAGE "<jar/zip files and directories separated by :>"
#else
#define BCP_MESSAGE "<directories separated by :>"
#endif

void showNonStandardOptions() {
    printf("  -Xbootclasspath:%s\n", BCP_MESSAGE);
    printf("\t\t   locations where to find the system classes\n");
    printf("  -Xbootclasspath/a:%s\n", BCP_MESSAGE);
    printf("\t\t   locations are appended to the bootstrap class path\n");
    printf("  -Xbootclasspath/p:%s\n", BCP_MESSAGE);
    printf("\t\t   locations are prepended to the bootstrap class path\n");
    printf("  -Xbootclasspath/c:%s\n", BCP_MESSAGE);
    printf("\t\t   locations where to find Classpath's classes\n");
    printf("  -Xbootclasspath/v:%s\n", BCP_MESSAGE);
    printf("\t\t   locations where to find JamVM's classes\n");
    printf("  -Xasyncgc\t   turn on asynchronous garbage collection\n");
    printf("  -Xcompactalways  always compact the heap when garbage-collecting\n");
    printf("  -Xnocompact\t   turn off heap-compaction\n");
#ifdef INLINING
    printf("  -Xnoinlining\t   turn off interpreter inlining\n");
    printf("  -Xshowreloc\t   show opcode relocatability\n");
    printf("  -Xreplication:[none|always|<value>]\n");
    printf("\t\t   none : always re-use super-instructions\n");
    printf("\t\t   always : never re-use super-instructions\n");
    printf("\t\t   <value> copy when usage reaches threshold value\n");
    printf("  -Xcodemem:[unlimited|<size>] (default maximum heapsize/4)\n");
#endif
    printf("  -Xms<size>\t   set the initial size of the heap\n");
    printf("\t\t   (default = MAX(physical memory/64, %dM))\n",
           DEFAULT_MIN_HEAP/MB);
    printf("  -Xmx<size>\t   set the maximum size of the heap\n");
    printf("\t\t   (default = MIN(physical memory/4, %dM))\n",
           DEFAULT_MAX_HEAP/MB);
    printf("  -Xss<size>\t   set the Java stack size for each thread "
           "(default = %dK)\n", DEFAULT_STACK/KB);
    printf("\t\t   size may be followed by K,k or M,m (e.g. 2M)\n");
}

void showUsage(char *name) {
    printf("Usage: %s [-options] class [arg1 arg2 ...]\n", name);
    printf("                 (to run a class file)\n");
    printf("   or  %s [-options] -jar jarfile [arg1 arg2 ...]\n", name);
    printf("                 (to run a standalone jar file)\n");
    printf("\nwhere options include:\n");
    printf("  -client\t   compatibility (ignored)\n");
    printf("  -server\t   compatibility (ignored)\n\n");
    printf("  -cp\t\t   <jar/zip files and directories separated by :>\n");
    printf("  -classpath\t   <jar/zip files and directories separated by :>\n");
    printf("\t\t   locations where to find application classes\n");
    printf("  -D<name>=<value> set a system property\n");
    printf("  -verbose[:class|gc|jni]\n");
    printf("\t\t   :class print out information about class loading, etc.\n");
    printf("\t\t   :gc print out results of garbage collection\n");
    printf("\t\t   :jni print out native method dynamic resolution\n");
    printf("  -version\t   print out version number and copyright information\n");
    printf("  -showversion     show version number and copyright and continue\n");
    printf("  -fullversion     show jpackage-compatible version number and exit\n");
    printf("  -? -help\t   print out this message\n");
    printf("  -X\t\t   show help on non-standard options\n");
}

void showVersionAndCopyright() {
    printf("java version \"%s\"\n", JAVA_COMPAT_VERSION);
    printf("JamVM version %s\n", VERSION);
    printf("Copyright (C) 2003-2014 Robert Lougher <rob@jamvm.org.uk>\n\n");
    printf("This program is free software; you can redistribute it and/or\n");
    printf("modify it under the terms of the GNU General Public License\n");
    printf("as published by the Free Software Foundation; either version 2,\n");
    printf("or (at your option) any later version.\n\n");
    printf("This program is distributed in the hope that it will be useful,\n");
    printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
    printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n");
    printf("GNU General Public License for more details.\n");
    printf("\nBuild information:\n\nExecution Engine: %s\n",
           getExecutionEngineName());

#if defined(__GNUC__) && defined(__VERSION__)
    printf("Compiled with: gcc %s\n", __VERSION__);
#endif

    printf("\nBoot Library Path: %s\n", classlibDefaultBootDllPath());
    printf("Boot Class Path: %s\n", classlibDefaultBootClassPath());
}

void showFullVersion() {
    printf("java full version \"jamvm-%s\"\n", JAVA_COMPAT_VERSION);
}

/**
 * 
 * 解析命令行参数
 * 
 **/
int parseCommandLine(int argc, char *argv[], InitArgs *args) {
    Property props[argc-1]; // 这是定义了参数个数大小的props数组
    int is_jar = FALSE; // 是否是jar包运行
    int status = 0; 
    int i;

    // 赋值 把数组的地址 复制到了初始化结构体当中
    args->commandline_props = &props[0];

    for(i = 1; i < argc; i++) {
        if(*argv[i] != '-') {
            // 非 -开头 则可能是配置之类的
            if(args->min_heap > args->max_heap) {
                // 最小的堆内存肯定不能大于最大的啦
                printf("Minimum heap size greater than max!\n");
                // 跳转到退出的代码块 status 1是返回给父进程的状态值。
                // TODO 如果给父进程返回1会怎么样？
                status = 1;
                goto exit; 
            }

            // commandline_props数组的长度 
            if(args->props_count) {
                // 如果commandline_props数组已经有数据
                // TODO 下面两个是啥操作
                args->commandline_props = sysMalloc(args->props_count *
                                                    sizeof(Property));
                memcpy(args->commandline_props, &props[0], args->props_count *
                                                           sizeof(Property));
            }

            // 如果是jar文件
            if(is_jar) {
                // TODO（存疑）则-jar的下一个参数肯定是jar包的目录了 把目录赋到配置中的classpath中？
                args->classpath = argv[i];
                // TODO 这个设置又是为何
                argv[i] = "jamvm/java/lang/JarLauncher";
            }

            // 返回的是 角标
            // 这个角标会用来干嘛？
            return i;
        }
        // 如果参数是以 - 开头 说明是一个指令或者配置性的东西
        switch(parseCommonOpts(argv[i], args, FALSE)) {
            case OPT_OK:
                // 结束
                break;
        	
            case OPT_ERROR:
                // 跳转到退出的代码块 status 1是返回给父进程的状态值。
                // TODO 如果给父进程返回1会怎么样？
        	    status = 1;
        	    goto exit;
        	
            case OPT_UNREC:
                // TODO 这个是啥状态啊
            default:
                if(strcmp(argv[i], "-?") == 0 ||
                   strcmp(argv[i], "-help") == 0) {
                    // help打印
                    goto usage;

                } else if(strcmp(argv[i], "-X") == 0) {
                    // 打印-X的帮助命令
                    showNonStandardOptions();
                    goto exit;

                } else if(strcmp(argv[i], "-version") == 0) {
                    // 版本信息 我编译出来的居然是1.5的，感觉是.configuration的时候指定的
                    showVersionAndCopyright();
                    goto exit;

                } else if(strcmp(argv[i], "-showversion") == 0) {
                    // 同上
                    showVersionAndCopyright();

                } else if(strcmp(argv[i], "-fullversion") == 0) {
                    showFullVersion();
                    goto exit;

                } else if(strncmp(argv[i], "-verbose", 8) == 0) {
                    char *type = &argv[i][8];

                    if(*type == '\0' || strcmp(type, ":class") == 0)
                        // class信息的打印
                        args->verboseclass = TRUE;

                    else if(strcmp(type, ":gc") == 0 || strcmp(type, "gc") == 0)
                        // gc信息的打印
                        args->verbosegc = TRUE;

                    else if(strcmp(type, ":jni") == 0)
                        // jni信息的打印 这个就是动态链接库啊？？
                        args->verbosedll = TRUE;

                } else if(strcmp(argv[i], "-jar") == 0) {
                    // 是jar文件
                    is_jar = TRUE;

                } else if(strcmp(argv[i], "-classpath") == 0 ||
                          strcmp(argv[i], "-cp") == 0) {

                    if(i == argc - 1) {
                        printf("%s : missing path list\n", argv[i]);
                        goto exit;
                    }
                    // 配置classpath 
                    // TODO配置了有什么用啊？
                    args->classpath = argv[++i];

                } else if(strncmp(argv[i], "-Xbootclasspath/c:", 18) == 0) {
                    // 这个跟下面那个应该都是跟classpath有关的
                    args->bootpath_c = argv[i] + 18;

                } else if(strncmp(argv[i], "-Xbootclasspath/v:", 18) == 0) {
                    args->bootpath_v = argv[i] + 18;

                /* Compatibility options 兼容性选项 */
                } else if(strcmp(argv[i], "-client") == 0 ||
                          strcmp(argv[i], "-server") == 0) {
                    /* Ignore 被忽略了？*/
                    // TODO 老实说 client和server的区别在哪？
                } else {
                    printf("Unrecognised command line option: %s\n", argv[i]);
                    status = 1;
                    goto usage;
                }
        }
    }

usage:
    showUsage(argv[0]);

exit:
    exit(status);
}

/**
 * argc 参数大小
 * argv[] 参数列表
 **/
int main(int argc, char *argv[]) {
    Class *array_class, *main_class;
    Object *system_loader, *array;
    MethodBlock *mb;
    InitArgs args;
    int class_arg;
    char *cpntr;
    int status;
    int i;

    // 看名字就知道 设置默认的初始化参数 传入了参数结构体的指针
    setDefaultInitArgs(&args);
    // 解析命令行 传入参数大小 参数列表 和一个参数结构体的指针
    // 这个方法返回的class_arg 好像就是要运行的class所在的参数的位置
    class_arg = parseCommandLine(argc, argv, &args);

    // TODO 这个是啥啊 看名字是主栈基....不晓得是啥
    args.main_stack_base = &array_class;

    // 初始化VM 里面做了很多初始化的工作（废话）老长老长了，到时候可以把这部分代码给写到博客当中
    // 就按照初始化的顺序写
    // 反正写博客的时候，就把这个方法的所有操作给写成大纲，然后每一个大纲下面的小纲就是每个方法里面要做的事情，如此一来骨架就有了
    if(!initVM(&args)) {
        // 初始化失败
        printf("Could not initialise VM.  Aborting.\n");
        exit(1);
    }
   
    // 看名字 获取系统的类加载器
    // 内部查找、装载、链接、初始化了ClassLoder这个class 然后执行ClassLoader.getSystemClassLoader() 获取到了一个classLoder
    if((system_loader = getSystemClassLoader()) == NULL)
        goto error;

    // 看名字 给主线程设置上下文class loder
    // 内部就是查找主线程的contextClassLoader属性，然后把system_loader赋过去
    mainThreadSetContextClassLoader(system_loader);

    // class_arg是class类的角标  例如 java -Xms=128M com.gonzo.hello.HelloWorld 角标就是1
    // 这个代码 就是把com.gonzo.hello.HelloWorld的'.'替换成'/'替换成专有的格式
    for(cpntr = argv[class_arg]; *cpntr; cpntr++)
        if(*cpntr == '.')
            *cpntr = '/';

    // 获取主函数的Class 从类加载器
    // 为啥是类加载器？
    main_class = findClassFromClassLoader(argv[class_arg], system_loader);
    if(main_class != NULL)
        // 如果类加载出来了当然就是取初始化了
        initClass(main_class);

    // 这个就是判断线程的ee是否存在exception 是的话就说明执行过程当中有异常发生了
    if(exceptionOccurred())
        goto error;

    // 这个看名字就知道了 查找主函数 这个和findMethod还是有点区别的 如果找不到会从父类找，但是findMethod不会
    mb = lookupMethod(main_class, SYMBOL(main),
                                  SYMBOL(_array_java_lang_String__V));

    // 判断main方法是否存在 以及判断方法的可访问权限是否是静态且可访问的
    if(mb == NULL || !(mb->access_flags & ACC_STATIC)) {
        // 不行的话，就是抛异常
        signalException(java_lang_NoSuchMethodError, "main");
        goto error;
    }

    /* Create the String array holding the command line args 
        创建包含命令行参数的字符串数组 */

    i = class_arg + 1; // 这个应该是准备多传一个参数进去所以+1的？
    // 获取[Ljava/lang/String;=String[] 这个是一个单独的类型？奇怪
    if((array_class = findArrayClass(SYMBOL(array_java_lang_String))) &&
            // TODO 给array 分配内存？
           (array = allocArray(array_class, argc - i, sizeof(Object*))))  {
        // TODO 这个应该是获取到数组的指针吧，不是很看的懂
        Object **args = ARRAY_DATA(array, Object*) - i;
        
        // 循环
        for(; i < argc; i++)
            // 看名字 把C的字符串转成java的String 
            // 然后把替换完的String赋值给args 也就是main函数的args
            if(!(args[i] = Cstr2String(argv[i])))
                break;

        /* Call the main method 调用main方法*/
        if(i == argc) // 如果全部否赋值成功
            // 执行静态方法~
            // 这里就是main方法啦 
            // TODO 这个方法里面的代码贼多 需要好好调试一下
            executeStaticMethod(main_class, mb, array);
    }

error:
    /* ExceptionOccurred returns the exception or NULL, which is OK
       for normal conditionals, but not here... 
       ExceptionOccurred返回异常或NULL，这对于正常的条件语句是可以的，但在这里不是… */
    if((status = exceptionOccurred() ? 1 : 0))
        uncaughtException();

    /* Wait for all but daemon threads to die 
        等待除守护线程外的所有线程死亡 */
    mainThreadWaitToExitVM();
    // 退出VM
    exitVM(status);

   /* Keep the compiler happy 
    让编译器高兴 
    哈哈哈哈哈哈*/
    return 0;
}
